package com.gitee.hermer.engine.jars.dependent.thread.pool.manage;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import com.gitee.hermer.boot.jee.commons.number.IntegerUtils;
import com.gitee.hermer.boot.jee.commons.number.NumberUtils;
import com.gitee.hermer.boot.jee.commons.verify.Assert;
import com.gitee.hermer.engine.jars.core.domain.EngineConstant;
import com.gitee.hermer.engine.jars.dependent.thread.pool.IThreadPoolManage;
import com.gitee.hermer.engine.jars.dependent.thread.pool.callback.CallableResult;
import com.gitee.hermer.engine.jars.dependent.thread.pool.config.ThreadPoolProperties;

@Component
public class ThreadPoolManage implements IThreadPoolManage{


	private ThreadPoolProperties properties;

	private ForkJoinPool forkJoinPool;

	@Override
	@PreDestroy
	public void destroy() throws Exception {
		if(forkJoinPool != null){
			forkJoinPool.shutdown();
			forkJoinPool = null;
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if(forkJoinPool == null && properties != null){
			forkJoinPool = new ForkJoinPool(IntegerUtils.defaultIfZero(properties.getPoolSize(), EngineConstant.DEFAULT_VALUES_ENGINE_CONFIG_THREAD_POOL_SIZE));
			
		}
	}

	@Override
	@Autowired
	public void setProperties(ThreadPoolProperties properties) {
		this.properties = properties;
	}


	public <T extends Callable<V>,V> V  execute(T task){
		return execute((ForkJoinTask<V>)RecursiveTask.adapt(task));
	}

	public <T extends Runnable> void execute(T task){
		execute((ForkJoinTask<?>)RecursiveTask.adapt(task));
	}
	
	public <T extends ForkJoinTask<V>,V> V execute(T task){
		return forkJoinPool.invoke(task);
	}
	

	@Override
	public <T extends RecursiveTask<V>, V> V execute(T task) {
		return forkJoinPool.invoke(task);
	}



	@Override
	public <T extends Runnable> ForkJoinTask<?> executeAsyn(T task) throws Exception {
		return executeAsyn(RecursiveTask.adapt(task));
	}

	@Override
	public <T extends Callable<V>,V> ForkJoinTask<V>  executeAsyn(T task) throws Exception{
		return executeAsyn(RecursiveTask.adapt(task));
	}

	public <T extends ForkJoinTask<V>,V> ForkJoinTask<V>  executeAsyn(T task) throws Exception{
		return forkJoinPool.submit(task);
	}



	public <T extends RecursiveTask<V>, V> ForkJoinTask<V> executeAsyn(T task) throws Exception{
		forkJoinPool.execute(task);
		return (ForkJoinTask<V>)task;
	}

	@Override
	public Integer getExecuteTimeOut() {
		return IntegerUtils.defaultIfZero(properties.getTimeout(), EngineConstant.DEFAULT_VALUES_ENGINE_CONFIG_THREAD_EXECUTE_TIMEOUT);
	}
	
	

	@Override
	public <T extends ForkJoinTask<V>, V> void executeAsyn(T task, final CallableResult<V> call) throws Exception {
		final ForkJoinTask<V> joinTask = executeAsyn((ForkJoinTask<V>)task);
		executeAsynResult(joinTask, call);
	}

	
	protected <T extends ForkJoinTask<V>, V> void executeAsynResult(ForkJoinTask<V> task,CallableResult<V> call) throws Exception{
		executeAsyn(new Runnable(){
			@Override
			public void run() {
				try {
					call.onCall(task.get(getExecuteTimeOut(), TimeUnit.MILLISECONDS));
				} catch (Exception e) {
					call.onError(e);
				}finally {
					call.done();
				}
			}
		});
	}
	
	@Override
	public <T extends Callable<V>, V> void executeAsyn(T task, CallableResult<V> call) throws Exception {
		final ForkJoinTask<V> joinTask = executeAsyn((Callable<V>)task);
		executeAsynResult(joinTask, call);
	}


	@Override
	public <T extends RecursiveTask<V>, V> void executeAsyn(T task, CallableResult<V> call) throws Exception{
		final ForkJoinTask<V> joinTask = executeAsyn((RecursiveTask<V>)task);
		executeAsynResult(joinTask, call);
	}


}
